﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using élénkPróbája.extentions;
using élénkPróbája.Test.dto;
using élénkPróbája.Test.extention;
using élénkPróbája.Test.helpers;
using élénkPróbája.validators;

namespace élénkPróbája.Test
{
    [TestClass]
    public class ConditionTester
    {
        [TestMethod]
        public void Validation_should_succeed_when_condition_does_not_match()
        {
            var validator = new TestConditionValidator();
            var result = validator.Validate(new Person { Id = 1 });
            Assert.IsTrue(result.IsValid);
        }
        [TestMethod]
        public void Validation_should_succeed_when_async_condition_does_not_match()
        {
            var validator = new TestConditionAsyncValidator();
            var result = validator.ValidateAsync(new Person { Id = 1 }).Result;
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Validation_should_fail_when_condition_matches()
        {
            var validator = new TestConditionValidator();
            var result = validator.Validate(new Person());
            result.IsValid.ShouldBeFalse();
        }
        [TestMethod]
        public void Validation_should_fail_when_async_condition_matches()
        {
            var validator = new TestConditionAsyncValidator();
            var result = validator.ValidateAsync(new Person()).Result;
            result.IsValid.ShouldBeFalse();
        }
        [TestMethod]
        public void Validation_should_succeed_when_condition_matches()
        {
            var validator = new InverseConditionValidator();
            var result = validator.Validate(new Person());
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Validation_should_succeed_when_async_condition_matches()
        {
            var validator = new InverseConditionAsyncValidator();
            var result = validator.ValidateAsync(new Person()).Result;
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Validation_should_fail_when_condition_does_not_match()
        {
            var validator = new InverseConditionValidator();
            var result = validator.Validate(new Person { Id = 1 });
            result.IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void Validation_should_fail_when_async_condition_does_not_match()
        {
            var validator = new InverseConditionAsyncValidator();
            var result = validator.ValidateAsync(new Person { Id = 1 }).Result;
            result.IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void Condition_is_applied_to_all_validators_in_the_chain()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull().NotEqual("foo").When(x => x.Id > 0)
            };

            var result = validator.Validate(new Company());
            result.Errors.Count.ShouldEqual(0);
        }

        [TestMethod]
        public void Async_condition_is_applied_to_all_validators_in_the_chain()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull().NotEqual("foo").WhenAsync(x => hTask.FromResult(x.Id > 0))
            };

            var result = validator.ValidateAsync(new Company()).Result;
            result.Errors.Count.ShouldEqual(0);
        }

        [TestMethod]
        public void Condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull().NotEqual("foo").When(x => x.Id > 0, ApplyConditionTo.CurrentValidator)
            };

            var result = validator.Validate(new Company());
            result.Errors.Count.ShouldEqual(1);
        }

        [TestMethod]
        public void Async_condition_is_applied_to_single_validator_in_the_chain_when_ApplyConditionTo_set_to_CurrentValidator()
        {
            var validator = new TestValidator {
                v => v.RuleFor(x => x.Name).NotNull().NotEqual("foo").WhenAsync(x => hTask.FromResult(x.Id > 0), ApplyConditionTo.CurrentValidator)
            };

            var result = validator.ValidateAsync(new Company()).Result;
            result.Errors.Count.ShouldEqual(1);
        }

        #region InternalClasses
        private class TestConditionValidator : AbstractValidator<Person>
        {
            public TestConditionValidator()
            {
                RuleFor(x => x.Forename).NotNull().When(x => x.Id == 0);
            }
        }

        class TestConditionAsyncValidator : AbstractValidator<Person>
        {
            public TestConditionAsyncValidator()
            {
                RuleFor(x => x.Forename).NotNull().WhenAsync(x => hTask.FromResult(x.Id == 0));
            }
        }

        private class InverseConditionValidator : AbstractValidator<Person>
        {
            public InverseConditionValidator()
            {
                RuleFor(x => x.Forename).NotNull().Unless(x => x.Id == 0);
            }
        }

        class InverseConditionAsyncValidator : AbstractValidator<Person>
        {
            public InverseConditionAsyncValidator()
            {
                RuleFor(x => x.Forename).NotNull().UnlessAsync(x => hTask.FromResult(x.Id == 0));
            }
        }
        #endregion
    }
}
